﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProgNetComponents.ExtenderProviders;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
using System.Data;
using DevExpress.XtraGrid.Columns;
using System.ComponentModel;
using ProgNetDxComponents.ExtenderProviders.Settings;

namespace ProgNetDxComponents.ExtenderProviders
{
    public class ChangeDetercorProvider : BaseExtenderProvider<Settings.ChangeDetectorSettings>
    {
        public ChangeDetercorProvider(IContainer container)
            : base(container)
        {
        }
         public ChangeDetercorProvider(){}
        private int _LockContext = 0;
        
        [Browsable(false)]
        public bool HasChanges
        {
            get { return Values.Count > 0; }
        }

        public bool DefaultDetectChanges
        {
            get;
            set;
        }

        private bool PrevHasChanges;

        Dictionary<object,object> Values = new Dictionary<object, object>();
        public event EventHandler ContextChanged;
        public event EventHandler HasChangesChanged;

        protected void OnContextChanged()
        {
            if (ContextChanged != null)
                ContextChanged(this, EventArgs.Empty);
        }
        protected void OnHasChangesChanged()
        {
            if (HasChangesChanged != null)
                HasChangesChanged(this, EventArgs.Empty);
        }
        protected override void ProcessObject(object sender, ChangeDetectorSettings settings)
        {
            if (sender is BaseEdit)
            {
                (sender as BaseEdit).EditValueChanging -= new DevExpress.XtraEditors.Controls.ChangingEventHandler(ChangeDetercorProvider_EditValueChanging);
                (sender as BaseEdit).EditValueChanging += new DevExpress.XtraEditors.Controls.ChangingEventHandler(ChangeDetercorProvider_EditValueChanging);
            }
            else if (sender is ColumnView)
            {
                (sender as ColumnView).CellValueChanging -= new CellValueChangedEventHandler(ChangeDetercorProvider_CellValueChanging);
                (sender as ColumnView).CellValueChanging += new CellValueChangedEventHandler(ChangeDetercorProvider_CellValueChanging);
                (sender as ColumnView).RowCountChanged -= new EventHandler(ChangeDetercorProvider_RowCountChanged);
                (sender as ColumnView).RowCountChanged += new EventHandler(ChangeDetercorProvider_RowCountChanged);
            }
        }

        void ChangeDetercorProvider_RowCountChanged(object sender, EventArgs e)
        {
            BaseView v = sender as BaseView;
            object key = GetRealKey(sender);
            if (key != null && _AllSettings.ContainsKey(key))
            {
                if (!_AllSettings[key].DetectChanges)
                    return;
            }

            if (v != null)
            {
                DataTable dt = v.GetTable();
                if (dt != null)
                {
                    bool hasAddedInsertedRows = dt.Select(null, null, DataViewRowState.Added | DataViewRowState.Deleted).Length > 0;
                    CheckChanges(dt, false, hasAddedInsertedRows);
                }
            }
        }

        void ChangeDetercorProvider_CellValueChanging(object sender, CellValueChangedEventArgs e)
        {
            ColumnView v = sender as ColumnView;
            if (v != null)
            {
                object key = GetRealKey(e.Column);
                if (GetSettings(key).DetectChanges)
                {
                    if (e.RowHandle == DevExpress.XtraGrid.GridControl.NewItemRowHandle)
                        return;
                    if (v.IsValidRowHandle(e.RowHandle))
                    {
                        DataRow r = v.GetDataRow(e.RowHandle);
                        if (r != null && r.RowState == DataRowState.Added || r.RowState == DataRowState.Deleted)
                            return;
                    }
                    string uniqueName = v.Name + "_" + e.Column.FieldName + "[" + e.RowHandle + "]";
                    object oldValue = v.GetRowCellValue(e.RowHandle, e.Column);

                    CheckChanges(uniqueName, oldValue, e.Value);
                }
            }
        }
        protected override object GetRealKey(object target)
        {
            object key = target;

            if (target is BaseView)
            {
                BaseView gv = target as BaseView;
                if (gv.SourceView != null)
                    key = gv.SourceView;
            }
            if (target is GridColumn)
            {
                if ((target as GridColumn).View != null && (target as GridColumn).View.SourceView != null)
                {
                    foreach (GridColumn c in ((target as GridColumn).View.SourceView as ColumnView).Columns)
                    {
                        if (c.Name == (target as GridColumn).Name)
                        {
                            return c;
                        }
                    }
                }
            }
            return key;
        }
        protected void CheckChanges(object target, object oldValue, object newValue)
        {
            if (_LockContext == 0)
            {
                PrevHasChanges = HasChanges;
                if (Values.ContainsKey(target))
                {
                    if (Values[target].IsTheSame(newValue))
                        Values.Remove(target);
                }
                else
                {
                    if (!oldValue.IsTheSame(newValue))
                        Values.Add(target, oldValue);
                }
                if (PrevHasChanges != HasChanges)
                    OnHasChangesChanged();
                PrevHasChanges = HasChanges;
                OnContextChanged();
            }
        }
        
        void ChangeDetercorProvider_EditValueChanging(object sender, DevExpress.XtraEditors.Controls.ChangingEventArgs e)
        {
            if (GetSettings(sender).DetectChanges)
            {
                CheckChanges(sender, e.OldValue, e.NewValue);
            }
        }
        protected override ChangeDetectorSettings CreateSettings(object owner)
        {
            ChangeDetectorSettings s = base.CreateSettings(owner);
            s.DetectChanges = DefaultDetectChanges;
            return s;
        }
        public override bool CanExtend(object extendee)
        {
            if(extendee is BaseEdit ||
                extendee is ColumnView ||
                extendee is GridColumn)
                return true;
            return false;
        }

        public void LockContext()
        {
            _LockContext++;
        }
        public void UnLockContext()
        {
            _LockContext--;
            if (_LockContext < 0)
                _LockContext = 0;
            OnContextChanged();
        }

        public void ResetAll()
        {
            Values.Clear();
            PrevHasChanges = false;
            OnHasChangesChanged();
            OnContextChanged();
        }
    }
}
